package com.tang.common.interceptor.frequency;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.ImmutableList;
import com.tang.common.async.LogInterfaceAsync;
import com.tang.common.constant.BusinessConstant;
import com.tang.common.constant.SafetyConstant;
import com.tang.common.event.LogInterfaceEvent;
import com.tang.common.exception.GeneralFilterHandlingException;
import com.tang.common.exception.GlobalException;
import com.tang.common.interceptor.AbstractInterceptorHandler;
import com.tang.common.utils.CommonUtils;
import com.tang.module.system.entity.InterfaceConfiguration;
import com.tang.module.system.entity.LogInterface;
import com.tang.module.system.entity.Role;
import com.tang.module.system.entity.User;
import com.tang.module.system.mapper.InterfaceConfigurationMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 该拦截器作用是验证对应用户的接口剩余次数验证
 * @author tang
 * @date 2022/1/21 13:44
 */
@Order(3)
@Component
public class FrequencyInterceptor extends AbstractInterceptorHandler {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private ApplicationEventPublisher applicationEvintPublisher;

    @Resource
    private InterfaceConfigurationMapper interfaceConfigurationMapper;

    @Autowired
    private LogInterfaceAsync logInterfaceAsync;


    @Override
    public void doFilter(HttpServletRequest request, HttpServletResponse response) {
        //访问uri
        String uriTag = request.getMethod() + request.getRequestURI();
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        //登录用户
        User user = (User) authentication.getPrincipal();
        //获取用户角色名称
        List<String> roleName = user.getRoleList().parallelStream().map(Role::getRoleName).collect(Collectors.toList());
        //获取接口类型
        Integer o = Integer.valueOf(String.valueOf(stringRedisTemplate.opsForHash().get(BusinessConstant.INTERFACE_RESTRICTION_HASH, uriTag)));
        //获取接口状态
        InterfaceConfiguration interfaceConfiguration = interfaceConfigurationMapper.selectOne(Wrappers.<InterfaceConfiguration>lambdaQuery().
                select(InterfaceConfiguration::getOpenLimit).
                eq(InterfaceConfiguration::getSign, request.getRequestURI()).
                eq(InterfaceConfiguration::getMethod, "GET".equals(request.getMethod()) ? 1 : "POST".equals(request.getMethod()) ? 2 : 3).
                eq(InterfaceConfiguration::getStatus,1));
        if (interfaceConfiguration == null){
            //未找到接口配置信息，直接放行，原因是缓存没有及时清除
            return;
        }
        //如果是超级管理员或者普通管理员,或者这个接口不需要访问控制，只需要记录日志
        if (!(/* roleName.contains(SafetyConstant.SUPER_ADMIN) ||
                roleName.contains(SafetyConstant.ADMIN)
                ||*/ !interfaceConfiguration.getOpenLimit())) {
            if (! canAccess(user,o)){
                throw new GeneralFilterHandlingException("抱歉,访问次数不足,请联系管理员");
            }
        }

//        //设置日志初始值
        LogInterface logInterface = new LogInterface();
        logInterface.setParameter(request.getParameterMap());
        logInterface.setUserId(user.getId());
        logInterface.setUserAccount(user.getAccount());
        logInterface.setUserNickname(user.getName());
        logInterface.setCreateTime(new Date());
        logInterface.setType(o);
        logInterface.setUrlTag(uriTag);
        logInterface.setRemoteIp(CommonUtils.getIpAddr(request));
        logInterface.setUserAgent(request.getHeader("User-Agent"));
        applicationEvintPublisher.publishEvent(new LogInterfaceEvent(logInterface));
    }

    public Boolean canAccess(User user,Integer type){
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        //设置返回值类型
        redisScript.setResultType(Long.class);
        //设置脚本内容
        redisScript.setLocation(new ClassPathResource("lua/interfaceRestrictions.lua"));
        //设置参数,执行脚本
        //返回值-2: 新用户; -1: 无访问次数; 大于等于0: 有访问次数
        Long execute = stringRedisTemplate.execute(redisScript, ImmutableList.of(BusinessConstant.INTERFACE_RESTRICTION_FREQUENCY, user.getId() + "-" + type));
        if (execute == null){
            throw  new GlobalException("redis脚本返回值异常");
        }
        if (execute == -1 || execute == -2){
            return false;
        }
        //异步更新数据库
        logInterfaceAsync.updateDatabase(user,type, Math.toIntExact(execute));
        return true;
    }

}
